/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.logic;

import com.blandware.atleap.webapp.util.core.WebappUtil;
import com.blandware.atleap.common.util.ConvertUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.taglib.TagUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;

/**
 * <p>Body of this tag will be rendered if and only if user is in specified role
 * or in a collection of roles.
 * </p>
 * <p>You can specify comma-separated list of roles in <b>roles</b> attribute just as
 * you can provide instance of <code>java.util.Collection</code> and <code>java.lang.Object[]</code>
 * as collection of roles or beans which have the property with name of the role.
 * All of these cases will be correctly handled.
 * </p>
 * <p>
 * If <code>var</code> attribute specified, tag body is ignored, and only
 * boolean result of check is assigned to given variable.
 * </p>
 * <p>
 * This tag has two modes: if <b>all</b> attribute is <code>false</code> (or not
 * specified), then user passes check if he/she has at least one role from
 * <b>roles</b>. If <b>all</b> attribute is <code>true</code>, then user must
 * have all roles from <b>roles</b> attribute to pass check.
 * </p>
 * <p>
 * Allowed attributes are:
 * <ul>
 * <li>
 * <b>roles</b> - required - defines a collection of roles that are allowed to
 * access body of this tag (see above)
 * </li>
 * <li>
 * <b>property</b> - name of property of beans in collection (if there are beans)
 * that contains role name
 * </li>
 * <li>
 * <b>all</b> - whether the user need to have all the roles from supplied
 * collection to pass check, or not. This can be "true" or "false", default is
 * "false".
 * </li>
 * <li>
 * <b>var</b> - name of scope variable to which result of check (allowed or not)
 * will be saved. If specified, tag body is ignored.
 * </li>
 * <li>
 * <b>scope</b> - scope of variable to which the result will be saved. If <b>var</b>
 * is omitted, this attribute is ignored.
 * </li>
 * </ul>
 * </p>
 * <p><a href="IsUserInRoleTag.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.3 $ $Date: 2006/08/03 10:08:24 $
 * @jsp.tag name="isUserInRole"
 * body-content="scriptless"
 */
public class IsUserInRoleTag extends SimpleTagSupport {

	protected transient final Log log = LogFactory.getLog(IsUserInRoleTag.class);

	/**
	 * Collection of roles. Body of tag will be rendered if and only if
	 * user belongs to at least one role in specified collection, except of
	 * cases when <code>all</code> attribute is set to <em>true</em>.
	 */
	protected Object roles;

	/**
	 * If role collection is specified and this attribute is set to true,
	 * body of tag will be rendered if user belongs to all roles in collection.
	 * If roles is omitted, setting this attribute will have no effect.
	 */
	protected Boolean all;

	/**
	 * Name of property of beans in collection (if there are beans) that contains role name.
	 */
	protected String property;

	/**
	 * Name of variable to export result of check (<code>Boolean.TRUE</code> or <code>Booolean.FALSE</code>)
	 */
	protected String var;

	/**
	 * Scope to export variable to. By default variable (if <code>var</code> attribute
	 * is specified) will be exported to page scope.
	 */
	protected String scope;

	/**
     * Returns roles that are allowed to view tag content
     *
	 * @return roles
     * @see #roles
	 * @jsp.attribute required="true"
	 * rtexprvalue="true"
	 * type="java.lang.Object"
	 * description="Collection of roles"
	 */
	public Object getRoles() {
		return roles;
	}

    /**
     * Sets roles that are allowed to view tag content
     *
     * @param roles roles to set
     * @see #roles
     */
	public void setRoles(Object roles) {
		this.roles = roles;
	}

	/**
     * Returns whether user must have all roles to gain access
     *
	 * @return whether user must have all roles to gain access
     * @see #all
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.Boolean"
	 * description="Whether or not user must belong to all roles in collection"
	 */
	public Boolean getAll() {
		return all;
	}

    /**
     * Sets whether user must have all roles to gain access
     *
     * @param all whether user must have all roles to gain access
     * @see #all
     */
	public void setAll(Boolean all) {
		this.all = all;
	}

	/**
     * Returns property name
     *
	 * @return property name
     * @see #property
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Property of beans in collection that contains role name"
	 */
	public String getProperty() {
		return property;
	}

    /**
     * Sets property name
     *
     * @param property property name to set
     * @see #property
     *
     */
	public void setProperty(String property) {
		this.property = property;
	}

	/**
     * Returns name of variable which will accept result of check
     *
	 * @return name of variable
     * @see #var
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Name of variable to export result of check"
	 */
	public String getVar() {
		return var;
	}

    /**
     * Sets name of variable which will accept result of check
     *
     * @param var name of variable to set
     * @see #var
     */
	public void setVar(String var) {
		this.var = var;
	}

	/**
     * Returns variable scope
     *
	 * @return variable scope
     * @see #scope
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Scope to export variable to"
	 */
	public String getScope() {
		return scope;
	}

    /**
     * Sets variable scope
     *
     * @param scope variable scope to set
     * @see #scope
     */
	public void setScope(String scope) {
		this.scope = scope;
	}

    /**
     * Processes the tag
     *
     * @throws JspException
     * @throws IOException
     */
	public void doTag() throws JspException, IOException {

		PageContext pageContext = (PageContext) getJspContext();

		if ( all == null ) {
			all = Boolean.FALSE;
		}

		HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();

		Collection rolesCollection = null;
		if ( roles instanceof String ) {
			rolesCollection = ConvertUtil.convertStringToList((String) roles, ",", true);
		} else if ( roles instanceof Collection ) {
			rolesCollection = ((Collection) roles);
		} else if ( roles instanceof Object[] ) {
			rolesCollection = Arrays.asList((Object[]) roles);
		} else {
			String errorMessage = "Only instances of 'java.util.Collection', " +
			        "'java.lang.Object[]' and 'java.lang.String' are supported in 'roles' attribute";
			JspTagException e = new JspTagException(errorMessage);
			throw e;
		}

		boolean isUserInRole = false;
		try {
			isUserInRole = WebappUtil.isUserInRole(rolesCollection, all.booleanValue(), property, request);
		} catch ( Exception e ) {
			JspTagException ex = new JspTagException(e);
			throw ex;
		}

		if ( var != null ) {
			int varScope = PageContext.PAGE_SCOPE;
			if ( scope != null ) {
				varScope = TagUtils.getInstance().getScope(scope);
			}
			pageContext.setAttribute(var, Boolean.valueOf(isUserInRole), varScope);
		} else if ( isUserInRole ) {
			JspFragment body = getJspBody();
			if ( body != null ) {
				body.invoke(null);
			}
		}
	}
}
